﻿/******************************************************************************
* SunnyUI.FrameDecoder 开源TCP、串口数据解码库。
* CopyRight (C) 2022-2023 ShenYongHua(沈永华).
* QQ群：56829229 QQ：17612584 EMail：SunnyUI@qq.com
*
* Blog:   https://www.cnblogs.com/yhuse
* Gitee:  https://gitee.com/yhuse/SunnyUI.FrameDecoder
*
* SunnyUI.FrameDecoder.dll can be used for free under the MIT license.
* If you use this code, please keep this note.
* 如果您使用此代码，请保留此说明。
******************************************************************************
* 文件名称: HeaderDelimiterFrameDecoder.cs
* 文件说明: 数据头部 - 数据帧解码器
* 当前版本: V1.0
* 创建日期: 2022-11-01
*
* 2022-11-01: V1.0.0 增加文件说明
******************************************************************************/

using System;

namespace Sunny.FrameDecoder
{
    /// <summary>
    /// 数据头部 - 数据帧解码器
    /// </summary>
    public class HeaderDelimiterFrameDecoder : BaseByteFrameDecoder
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="headers">数据头部</param>
        /// <param name="maxFrameLength">最大数据长度，仅判断数据长度</param>
        public HeaderDelimiterFrameDecoder(byte[] headers, int maxFrameLength = 0)
        {
            if (headers == null || headers.Length == 0)
            {
                throw new ArgumentNullException("Headers cannot be empty.");
            }

            Headers = headers;
            HeaderLength = headers.Length;
            MaxFrameLength = maxFrameLength;
            FrameExceptDataLength = HeaderLength;
        }

        private int HeaderLength;

        /// <summary>
        /// 分隔符。
        /// </summary>
        public byte[] Headers { get; }

        /// <summary>
        /// 解码函数
        /// </summary>
        protected override void Decoding()
        {
            if (Cache.WrittenCount < FrameExceptDataLength) return;
            int index = Cache.WrittenSpan.IndexOf(Headers);
            if (index > 0) Cache.Remove(index);
            if (Cache.WrittenCount < FrameExceptDataLength) return;
            index = Cache.WrittenSpan.IndexOf(HeaderLength, Headers);

            while (index >= 0)
            {
                if (MinFrameLength > 0 && index < MinFrameLength)
                {
                    DecoderError("The data length is less than the minimum length", Cache.WrittenSpan.ToArray());
                    Cache.Remove(index);
                    continue;
                }

                if (MaxFrameLength > 0 && MaxFrameLength > MinFrameLength && index > MaxFrameLength)
                {
                    DecoderError("The data length is greater than the maximum length.", Cache.WrittenSpan.ToArray());
                    Cache.Remove(index);
                    continue;
                }

                LastDecodedTime = DateTime.Now;
                var e = new HeaderDelimiterByteFrameDataEventArgs(
                    this,
                    Headers,
                    Cache.WrittenSpan.Slice(HeaderLength, index).ToArray(),
                    Cache.WrittenSpan.Slice(0, index + FrameExceptDataLength).ToArray());
                DoDecoder(this, e);

                Cache.Remove(HeaderLength + index);
                if (Cache.WrittenCount < FrameExceptDataLength) break;
                index = Cache.WrittenSpan.IndexOf(HeaderLength, Headers);
            }
        }

        /// <summary>
        /// 准备数据解码
        /// </summary>
        /// <param name="data">输入数据</param>
        /// <returns>判断此数据是否可以解码</returns>
        protected override bool PrepareDecode(ref byte[] data)
        {
            if (data == null || data.Length == 0)
            {
                DecoderError("The data to be decoded is null.", data);
                return false;
            }

            if (Cache.WrittenCount > 0)
            {
                return true;
            }

            if (data.AsSpan().IndexOf(Headers.AsSpan()) < 0)
            {
                DecoderError("Data header verification failed.", data);
                return false;
            }

            return true;
        }

        /// <summary>
        /// 根据数据创建完整帧数据
        /// </summary>
        /// <param name="data">数据</param>
        /// <returns>完整帧数据</returns>
        public byte[] CreateFrame(byte[] data)
        {
            byte[] frame = new byte[FrameExceptDataLength + data.Length];
            Array.Copy(Headers, 0, frame, 0, HeaderLength);
            Array.Copy(data, 0, frame, HeaderLength, data.Length);
            return frame;
        }

        /// <summary>
        /// 解析完整帧数据
        /// </summary>
        /// <param name="frame">完整帧数据</param>
        /// <param name="args">帧解码</param>
        /// <returns>解析是否成功</returns>
        public bool DecodeFrame(byte[] frame, out HeaderDelimiterByteFrameDataEventArgs args)
        {
            args = null;
            int idx = frame.AsSpan().IndexOf(Headers);
            if (idx == 0 && frame.Length >= Headers.Length)
            {
                args = new HeaderDelimiterByteFrameDataEventArgs(
                    this,
                    Headers,
                    frame.AsSpan().Slice(Headers.Length).ToArray(),
                    frame);
            }

            return args != null;
        }
    }
}
